﻿using System;
using System.Collections.Generic;
using SlimDX.DirectInput;

namespace TileGert
{
  /// <summary>
  /// GertJoystickInput is a simple joystick input class that uses DirectInput.
  /// All attached Joystocks and Gamepads are used automatically. Use the indexer to access a specific
  /// joysticks data. The Count property tells you how many joysticks have been opened.
  /// The state of the joysticks is not automatically updated so if you want up-to-date data, you
  /// need to call the Update method. For example you can call Update once every frame of your game.
  /// </summary>
  public class GertJoystickInput : IEnumerable<GertJoystick>
  {
    private GertGame _game = null;
    private List<Device<JoystickState>> _joysticks = null;
    private List<JoystickState> _states = null;
    private List<GertJoystick> _gertSticks = null;

    /// <summary>
    /// Create a new joystick input class.
    /// </summary>
    public GertJoystickInput(GertGame game)
    {
      _game = game;
      _joysticks = new List<Device<JoystickState>>();
      _states = new List<JoystickState>();
      _gertSticks = new List<GertJoystick>();
      
      try
      {
        InputDeviceCollection allDevices = DirectInput.GetDevices(DeviceClass.GameController, DeviceEnumerationFlags.AttachedOnly);
        foreach (DeviceInstance instance in allDevices)
        {
          Device<JoystickState> dev = new Device<JoystickState>(instance.InstanceGuid);
          dev.Acquire();
          _joysticks.Add(dev);
          _states.Add(null);
          _gertSticks.Add(null);
        }
      }
      catch (Exception ex)
      {
        _game.Logger.LogLine("DirectInput devices for the joysticks could not be created: " + ex.Message, 0);
      }
    }
    
    /// <summary>
    /// Lazyly gets the current state of the joystick with exception handling.
    /// </summary>
    /// <param name="index">The index of the joystick.</param>
    /// <returns>The current joystick state or null if an exception occurred.</returns>
    private JoystickState GetJoystickState(int index)
    {
      if (_states[index] == null)
      {
        try
        {
          _states[index] = _joysticks[index].GetCurrentState();
        }
        catch (Exception ex)
        {
          _game.Logger.LogLine("JoystickState " + index + " could not be read: " + ex.Message, 1);
          return null;
        }
      }
      return _states[index];
    }

    /// <summary>
    /// Updates the joystick states that are used to get the joystick information.
    /// </summary>
    public void Update()
    {
      for (int i = 0; i < _states.Count; i++)
        _states[i] = null;
    }
    
    /// <summary>
    /// Gets the number of attached joysticks that are opened.
    /// </summary>
    public int Count
    {
      get { return _joysticks.Count; }
    }
    
    /// <summary>
    /// Gets the name of the joystick with the given index for display.
    /// </summary>
    /// <param name="index">The index of the joystick.</param>
    /// <returns></returns>
    public string GetName(int index)
    {
      return _joysticks[index].DeviceInformation.InstanceName;
    }
    
    /// <summary>
    /// Gets the current state of the joystick with the given index.
    /// </summary>
    public GertJoystick this[int index]
    {
      get
      {
        GertJoystick result = null;
        if (_gertSticks[index] == null)
          _gertSticks[index] = new GertJoystick();
        result = _gertSticks[index];
        result.State = GetJoystickState(index);
        return result;
      }
    }
    
    #region IEnumerable
    
    IEnumerator<GertJoystick> IEnumerable<GertJoystick>.GetEnumerator()
    {
      return _gertSticks.GetEnumerator();
    }
      
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
      return _gertSticks.GetEnumerator();
    }
    
    #endregion
    
  }
}
